#include "stmt.h"

void stmt_print ( struct stmt * s)
{
  if (!s) return;
  int print_semi = 0;  
  switch(s->kind)
  {
    case STMT_KIND_DECL:
      decl_print(s->decl);
      break;
    case STMT_KIND_EXPR:
      expr_print(s->expr);
      print_semi = 1;
      break;
    case STMT_KIND_IF_ELSE:
      printf("if (");
      expr_print(s->expr);
      printf(") ");
      stmt_print(s->body);
      if (s->ebody)
      {
        printf( " else ");
        stmt_print(s->ebody);
      }
      break;
    case STMT_KIND_WHILE:
      printf("while (");
      expr_print(s->expr);
      printf(")\n");
      stmt_print(s->body);
      break;
    case STMT_KIND_RETURN:
      printf("return ");
      expr_print(s->expr);
      print_semi = 1;
      break;
    case STMT_KIND_BLOCK:
      printf("\n{\n");
      stmt_print(s->body);
      printf("\n}");
      break;
    case STMT_KIND_PRINT:
      printf("print "); 
      expr_print(s->expr);
      print_semi = 1;
      break;
  }
  if (print_semi)  printf(";\n");
  stmt_print(s->next);
  
}


struct stmt * stmt_create ( 
  int kind, 
  struct decl * decl, 
  struct expr * expr, 
  struct stmt * body, 
  struct stmt * ebody, 
  struct stmt * next )
{
  struct stmt *a = malloc(sizeof(*a));

	a->kind = kind;
	a->decl = decl;
	a->expr = expr;
	a->body = body;
	a->ebody = ebody;
	a->next = next;

	return a;
}


void stmt_resolve(struct stmt * s, int print)
{
  if (!s) return;
  switch (s->kind)
  {
    case STMT_KIND_DECL:
      decl_resolve(s->decl, print);
      break;
    case STMT_KIND_BLOCK:
      //printf("scope level: %d\n", scope_level() );
      scope_enter();
      stmt_resolve(s->body, print);
      scope_exit();
      break;
    // a bunch of fall-through on the next cases:
    //remove fallthrough, add scoping? not sure
    case STMT_KIND_IF_ELSE:
      stmt_resolve(s->ebody, print);

    case STMT_KIND_WHILE:
      stmt_resolve(s->body, print);

    case STMT_KIND_PRINT:
    case STMT_KIND_RETURN:
    case STMT_KIND_EXPR:
      expr_resolve(s->expr, print);
      break;
  }
  stmt_resolve(s->next, print);
}

void stmt_typecheck(struct stmt * s, struct type * return_type)
{
  if (!s) return;
  struct type * e_val;
  struct type * VOIDIE = type_create(TYPE_KIND_VOID, 0, 0);
  switch (s->kind)
  {
    case STMT_KIND_IF_ELSE:
      stmt_typecheck(s->ebody, return_type);
    case STMT_KIND_WHILE:
      if (s->expr)
        if (!type_compare(expr_typecheck(s->expr), type_create(TYPE_KIND_BOOLEAN, 0, 0)) )
        {
  	  printf("Error: the conditional of an if statement or a while loop must be boolean.\n)");
          //printf("Yours is ");
          //type_print(expr_typecheck(s->expr));
          //printf(".\n");
	  exit(1);
        }
      stmt_typecheck(s->body, return_type);
      break;  
    case STMT_KIND_EXPR:
      expr_typecheck(s->expr);
      break;
    case STMT_KIND_BLOCK:
      stmt_typecheck(s->body, return_type);
      break;
    case STMT_KIND_DECL:
      decl_typecheck(s->decl);
      break;
    case STMT_KIND_PRINT:
      e_val = expr_typecheck(s->expr);
      if (type_compare(e_val, VOIDIE) )
      {
        printf("error: Args cannot be have type void.\n");
        exit(1);
      }
      break;
    case STMT_KIND_RETURN:
      e_val = expr_typecheck(s->expr);
      int errorflag = 0;
      if (!e_val)
      {
	//we know e_val is void/nonexistent
	if (!type_compare(return_type, VOIDIE))
        {
	  printf("Error: missing return expression\n");
	  printf("Specify a return value for the function\n");
          exit(1);
	}
      }
      else //handling e_val is not void
      {
        if ( !type_compare(return_type, e_val) )
        {
          printf("Error: function return type mismatch: ");
          type_print(return_type); printf(" vs. ");
          type_print(e_val); printf(".\n");
	  exit(1);
        }
      }
      break;
  }
  stmt_typecheck(s->next, return_type);
}

stmt_codegen(FILE * file, struct stmt * s)
{
  if (!s) return;
  switch(s->kind)
  {
    case STMT_KIND_EXPR:
      expr_codegen(s->expr);
      register_free(s->expr->reg);
      break;
    case STMT_KIND_DECL:
      decl_gen(s->decl);
      break;
    case STMT_KIND_BLOCK:
      stmt_gen(s->body);
    case STMT_KIND_RETURN:
      if (s->expr)
      {
        expr_gen(s->expr);
        fprintf(file, "movl %s, %%eax\n", register_name(s->expr->reg) );
        register_free(s-expr->reg);
        //maybe need to pop args from stack?
        fprintf(file, "LEAVE\n");
        fprintf(file, "RET\n");
  }
}
